Skip to content

Merge main into alpha/v2.0.0#325

Merged
heyitsaamir merged 13 commits intoalpha/v2.0.0from
main
Mar 25, 2026
Merged

Merge main into alpha/v2.0.0#325
heyitsaamir merged 13 commits intoalpha/v2.0.0from
main

Conversation

@heyitsaamir
Copy link
Collaborator

@heyitsaamir heyitsaamir commented Mar 25, 2026

Summary

Merge latest changes from main into alpha/v2.0.0 to produce the next alpha release.

What's included

Breaking changes

Features

Bug fixes

Other

Next steps

  1. Merge this PR
  2. Trigger the publish pipeline on alpha/v2.0.0
  3. Select Internal or Public publish type

🤖 Generated with Claude Code

heyitsaamir and others added 12 commits March 11, 2026 16:02
It’s time we start formalizing our relationship with Claude :). This PR
initializes a simple .claude.md with some common rules that claude tends
to get wrong often.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
before, when you do `Ctrl-C` after running the sample via the terminal,
it would throw a giant trace:

<img width="1469" height="883" alt="image"
src="https://github.com/user-attachments/assets/4760dc08-7534-4301-9a95-61d60ff6d7a1"
/>

now, with this update:

<img width="791" height="369" alt="image"
src="https://github.com/user-attachments/assets/3c11b786-1ba0-46b8-a29a-1e5281a05255"
/>

---------

Co-authored-by: lilydu <lilydu+odspmdb@microsoft.com>
This is a python adaptation of
microsoft/teams.ts#424.

Separate activity sending from HTTP transport layer

The previous architecture tightly coupled HTTP transport concerns with
activity sending logic:

**Previous Architecture:**
```
HttpPlugin (transport) → implements ISender (sending)
                      → has send() method (creates new Client per call)
                      → has createStream() method
                      → knows about Activity protocol details

ActivityContext → depends on ISender plugin
               → cannot work without transport plugin
               → conflates transport and sending concerns
```

There are a few issues with this:
- HttpPlugin created NEW Client instances on every send() call. So
there's really no benefit of this logic being in the "httpclient"
plugin.
- Transport plugins (HttpPlugin) were forced to implement
send/createStream. This makes it more cumbersome to build your own
HttpPlugin with your own servier.
- Users couldn't "bring their own server" without implementing ISender
- ActivityContext was tightly coupled to plugin architecture. ("Sender"
was coupled with an activity, without any necessary benefits.)

## New Architecture

```
HttpPlugin (transport) → only handles HTTP server/routing/auth
                      → emits CoreActivity (minimal protocol knowledge)
                      → just passes body payload to app

ActivitySender (NEW)  → dedicated class for sending activities
                     → receives injected, reusable Client
                     → handles all send/stream logic
                     → private to App class

ActivityContext       → uses ActivitySender now, which is not a plugin
```

In this PR, I am mainly decoupling responsibilities of HttpPlugin from
being BOTH a listener AND a sender, to being just a listener. The sender
bit is now separated to a different `ActivitySender` class. Other than
better code organization, the main thing this lets us do is **not
require the app to run to be able to send proactive messages**. This is
a huge plus point because now the App can be used in scenarios where it
doesn't necessarily need to _listen_ to incoming messages (like agentic
notifications!)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- **Replace `HttpPlugin` with `HttpServer` + `HttpServerAdapter`
pattern** — follows the [TS PR
#433](AzureAD/microsoft-authentication-library-for-js#433)
approach
- `HttpServer` is no longer a plugin — it's owned directly by `App`
- Introduces `HttpServerAdapter` protocol with `FastAPIAdapter` as the
default implementation
- Plugins inject `HttpServer` via DI and access the framework via
`server.adapter`
- Framework-agnostic handler pattern: `async (HttpRequest) ->
HttpResponse`
- Rewrites `remote_function_jwt_middleware` to be framework-agnostic
- Adds `examples/http-adapters/` with Starlette adapter and non-managed
FastAPI examples

Stacked on #249

## Test plan
- [x] Update unit tests
- [x] Run `poe test` — all tests pass
- [x] Run AI sample end-to-end
- [x] Run MCP sample end-to-end
- [x] Run Echo Bot sample end-to-end
- [x] Run Dialog sample end-to-end
- [x] Run Tab sample end-to-end
- [x] Run BotBuilder sample end-to-end

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…sages as preview (#306)

- `ExperimentWarning` is a subclass to `FutureWarning` so users supress
preview API warnings if desired.
- @experimental("ExperimentalTeamsReactions") decorator - emits warnings
when applied to a class or function
- Mark reactions and targeted messages as preview

Note that suppression is not granular - while C# differentiates between
different diagnostics, the ignore below will suppres all preview
warnings.

To suppress the warnings:

```python
import warnings
from microsoft_teams.common.experimental import ExperimentalWarning

warnings.filterwarnings("ignore", category=ExperimentalWarning)
```

---------

Co-authored-by: Corina Gum <>
## Summary
- Make `HttpServer.handle_request` public so plugins can route through
SDK-level JWT validation
- Change DI from `HttpServerAdapter` to `HttpServer` — plugins now
inject `HttpServer` and access the adapter via `.adapter`
- BotBuilderPlugin runs CloudAdapter auth first, then calls
`http_server.handle_request()` for SDK auth + Teams pipeline (previously
bypassed `TokenValidator` entirely)
- Remove pass-through methods (`register_route`, `serve_static`,
`start`, `stop`) from `HttpServer` — callers go through `server.adapter`
directly

## Test plan
- [x] `uv run pyright` — no new type errors
- [x] `poe test` — all apps, botbuilder, mcpplugin tests pass (153
tests)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Previously, we had a custom logger available for customers. However,
this didn't integrate very well with the general industry - other
libraries and frameworks use python's `logging` library (e.g., fastapi,
boto3, etc), and most developers are familiar with it. In addition,
using the standard library is better for reliability, and requires less
maintenance.

- updated to use module level logging throughout the packages (removed
from constructors, dependency injections)
- removed the `ConsoleLogger`, `LoggerDependencyOption`, and
`EventEmitterOptions` (it only has a logger property)
- kept the `ConsoleFormatter` and `ConsoleFilter` as an option for
developers to use
- added the `NullHandler` so that no errors/warnings will show up when
logger has not yet been configured (added to every package init file)
- updated common/README to explain how to setup and customize the logger
- removed mock loggers from tests
- regenerated the handlers now that logger is no longer a property

example w/ standard logger, using our custom formatter:
<img width="1165" height="300" alt="image"
src="https://github.com/user-attachments/assets/5cf51fc7-ea0c-4c36-8cc6-b93b040f31ed"
/>

Sharing notes/observations. I looked at the most popular python
libraries, as well as the most popular multi-package sdks.

**Pattern 1 - Single top-level logger (FastAPI, httpx, stripe)**
Simple, all log output is under one namespace, but all of the output is
in one undifferentiated stream. No granularity.
**Pattern 2 - Per module logger (OpenAI, Anthropic, urllib4, botocore,
azure sdks, google cloud sdks)**
Fine-grained, names expose the internal implementation details
**Pattern 3 - Central log.py file, with named subsystems (aiohttp)**
Stable, intuitive because users filter by what the system is doing since
all logger definitions live in one spot.
Very much overkill, requires more coordination, rare design if
subsystems don't need such independent filtering.

If users don't want to see any logs, thats what `NullHandler` is for -
logs are silent and python warnings are suppressed.

**When users want to see all the logs:**
logging.getLogger("microsoft_teams").setLevel(logging.DEBUG)

**When users want to narrow it down:**
logging.getLogger("microsoft_teams.ai").setLevel(logging.DEBUG)

---------

Co-authored-by: lilydu <lilydu+odspmdb@microsoft.com>
closes: #64

- added code and branch coverage tracking via pytest-cov
- added to CI, threshold set to 74% with current stat, should raise to
80% once more tests are added

## Usage
```bash
# Run tests with coverage report
uv run poe test-cov

# Generate XML coverage for CI
uv run poe test-cov-xml

# Open HTML coverage report
open htmlcov/index.html OR start htmlcov/index.html
```
current coverage: 74.33%

## Next Steps
1. add tests for untested packages (a2aprotocol, devtools)
2. improve coverage for minimally-tested packages (ai, botbuilder,
graph, mcpplugin)

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: lilydu <lilydu+odspmdb@microsoft.com>
Co-authored-by: Lily Du <lilyyduu@gmail.com>
Co-authored-by: heyitsaamir <ajawaid191@gmail.com>
Moved IsTargeted from Activity to Account
Updated routing logic to read from Recipient.IsTargeted instead of
Activity.IsTargeted
Fixes a bug where API client URLs could contain double slashes if the
`service_url` parameter had a trailing slash (e.g.,
`https://service.url/` would produce
`https://service.url//v3/conversations/...`).

## Changes Made

- **URL normalization**: Added `.rstrip("/")` when assigning
`service_url` in the `__init__` of all API clients:
`ConversationActivityClient`, `ConversationClient`,
`ConversationMemberClient`, `MeetingClient`, `TeamClient`,
`ReactionClient`, and `ApiClient`.
- **Sub-client propagation**: Updated `ConversationClient` and
`ApiClient` to pass the already-normalized `self.service_url` to
sub-client constructors, ensuring consistency throughout the client
hierarchy.
- **Tests**: Added tests to `test_conversation_client.py`,
`test_reaction_client.py`, `test_team_client.py`, and
`test_meeting_client.py` to verify that trailing slashes are stripped
and no double slashes appear in constructed URLs.

## Testing

- ✅ All 128 existing API unit tests pass
- ✅ New tests verify trailing slash stripping across all affected
clients
- ✅ Ruff linting and formatting checks pass
- ✅ CodeQL security scan found no issues

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: heyitsaamir <48929123+heyitsaamir@users.noreply.github.com>
## Summary
- Upgrade transitive dependencies to resolve 8 of 9 known
vulnerabilities found via `pip-audit`
- Raise minimum version floors for `pyjwt` and `fastmcp` to prevent
vulnerable versions from resolving on fresh installs
- Fix stale test from #318 that expected removed recipient-inference
behavior

## Vulnerabilities Fixed

| Package | Before | After | CVE |
|---------|--------|-------|-----|
| authlib | 1.6.6 | 1.6.9 | CVE-2026-28802 (JWT `alg:none` bypass) |
| cryptography | 46.0.3 | 46.0.5 | CVE-2026-26007 (EC subgroup
validation) |
| fastmcp | 2.14.0 | 3.1.1 | CVE-2025-69196 |
| pyjwt | 2.10.1 | 2.12.1 | CVE-2026-32597 (`crit` header bypass) |
| pyasn1 | 0.6.2 | 0.6.3 | CVE-2026-30922 (recursion bomb DoS) |
| starlette | 0.49.1 | 1.0.0 | CVE-2025-43859 (HTTP request smuggling) |
| fastapi | 0.128.0 | 0.135.2 | (pulled by starlette) |
| diskcache | 5.6.3 | removed | CVE-2025-69872 (no longer needed with
fastmcp 3.x) |

**Unfixable:** jsonpickle 1.4.2 (CVE-2020-22083) — pinned by
`botbuilder-core` to `>=1.2,<1.5`.

## Floor Constraint Changes
- `pyjwt[crypto]>=2.12.0` in api, apps, graph (was `>=2.10.0`)
- `fastmcp>=2.14.2` in mcpplugin (was `>=0.5.0`)

## Test plan
- [x] `poe test` — 546 passed, 0 failed
- [x] E2E tested echo, mcp-server, mcp-client, botbuilder examples
against live Teams

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Currently `/api/messages` is hardcoded as the messaging endpoint path.
This might not be always what a developer wants.
- Adds `messaging_endpoint` option to `AppOptions` (defaults to
`/api/messages`)
- `App` is the source of truth for the default; `HttpServer` requires it
explicitly
- `BotBuilderPlugin` reads the path from
`http_server.messaging_endpoint` instead of hardcoding

Port of microsoft/teams.ts#483

## Test plan
- [x] Existing `test_http_server.py` tests pass
- [x] New test for default messaging endpoint value
- [x] New test for custom messaging endpoint registration
- [x] Manual test with a custom endpoint path

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@heyitsaamir heyitsaamir marked this pull request as ready for review March 25, 2026 04:21
@heyitsaamir heyitsaamir merged commit 94be2d6 into alpha/v2.0.0 Mar 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants